/**
 * 
 */
package com.novartis.nims.imagedc.logic;

import java.io.File;
import java.io.IOException;
import java.net.URI;

import com.pixelmed.dicom.Attribute;
import com.pixelmed.dicom.AttributeList;
import com.pixelmed.dicom.DicomException;
import com.pixelmed.dicom.DicomInputStream;
import com.pixelmed.dicom.TagFromName;
import com.pixelmed.display.SourceImage;

/**
 * This class abstracts the notion of a DICOM image resource which provides a
 * DICOM dataset. The resource is identified by a URI, although this
 * implementation only supports <code>file:</code> type resources. The resource
 * can be queried for attribute values using the {@link
 * #getAttributeValue(AttributeTagDelegate, boolean) getAttributeValue} method.
 * 
 * @author Josh Snyder
 */
public class ImageResource {

	private final URI uri;
	
	//the attribute list will be lazy loaded
	private AttributeList fileAttributes = null;
	
	/**
	 * Constructs the image resource identified by the provided URI.
	 * 
	 * @param theUri
	 * @throws IllegalArgumentException if the form of URI provided is not supported
	 */
	public ImageResource(URI theUri) {
		if (! theUri.isAbsolute()) {
			throw new IllegalArgumentException("Resources identified by relative URIs are not supported.");
		}
		if (! theUri.getScheme().equals("file")) {
			throw new IllegalArgumentException("Only file resources are supported, not " + theUri.getScheme() + " .");
		}
		uri = theUri;
	}
	
	/**
	 * @return the URI identifying the image resource
	 */
	public URI getUri() {
		return uri;
	}
	
	/**
	 * Indicates whether some other object is "equal to" this one.
	 * <p>
	 * Another object is considered equal to this one if it is an <code>
	 * instanceof</code> this class or a subclass and if its URI is equal to
	 * this object's URI.
	 * <p>
	 * Subclasses must be careful to preserve symmetry when overriding
	 * this method.
	 * 
	 * @see java.net.URI#equals(Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof ImageResource) {
			return uri.equals(((ImageResource)obj).uri);
		} else {
			return false;
		}
	}

	/**
	 * Returns the hash code value for this object. The hash code is defined as
	 * the hash code of the URI this object contains.
	 * 
	 * @see java.net.URI#hashCode()
	 */
	@Override
	public int hashCode() {
		return uri.hashCode();
	}
	
	//lazy loads the attribute list constituting the DICOM dataset this resource
	//identifies.
	private AttributeList attributes(boolean ignoreBadPixelData) throws IOException, DicomException {
		if (fileAttributes == null) {
			fileAttributes = new AttributeList();
			
			DicomInputStream dis = new DicomInputStream(new File(uri));
			try {
				try {
					fileAttributes.read(dis);
				} catch (Exception e) {
					//if the full read fails, the pixel data may be corrupt.
					//in this case, the pixel data may optionally be skipped in a retry
					if (ignoreBadPixelData) fileAttributes.read(dis, TagFromName.PixelData);
				}
			} finally {
				dis.close();
			}
		}

		return fileAttributes;
	}
	
	/**
	 * Gets a single string value for the attribute associated with the tag.
	 * If there is more than one string value, only the first is returned. If
	 * there is no string value, the value is absent, or it cannot be read for
	 * some reason, an empty string is returned.
	 * <p>
	 * A canonicalized (unpadded) form is returned, not the original string.
	 * 
	 * @param tag the DICOM tag describing which attribute's value should be fetched.
	 * @param ignoreBadPixelData a flag controlling whether failed attempts to
	 * read from the image resource should be retried, skipping the pixel data.
	 * @return the first value of the attribute, or an empty <code>String</code>
	 * @throws IOException if the file cannot be read
	 * @throws DicomException
	 */
	public String getAttributeValue(AttributeTagDelegate tag, boolean ignoreBadPixelData) throws IOException, DicomException {
		Attribute attr = attributes(ignoreBadPixelData).get(tag.getAttributeTag());
		
		String value = null;
		if (attr != null) {
			value = attr.getSingleStringValueOrNull();
		}
		
		return (attr == null || value == null) ? "" : value;
	}
	
	public SourceImage getImage() throws DicomException, IOException {
		SourceImage image = null;
		
		AttributeList attrList = attributes(false);
		if (attrList.get(TagFromName.PixelData) != null) {
			image = new SourceImage(attrList);
		}
		
		return image;
	}

}
